package com.aiwiown.snackmq.cluster.raft.entity;

import com.aiwiown.snackmq.common.protocol.OperationType;
import com.aiwiown.snackmq.common.serialization.JsonSerializer;
import com.alipay.sofa.jraft.Closure;
import com.alipay.sofa.jraft.entity.Task;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.nio.ByteBuffer;

/**
 * Raft 日志条目实体类。
 * 用于在 Raft 协议中记录操作日志，每个日志条目都包含一个操作类型和相应的数据。
 */
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class SnackMQRaftLogEntry implements Serializable {

    private static final JsonSerializer serializer = new JsonSerializer();
    private static final long serialVersionUID = 3345221773246135034L;

    private OperationType operationType; // 操作类型
    private byte[] data; // 存储序列化后的数据

    /**
     * 构造函数，用于创建一个 SnackMQRaftLogEntry 对象。
     * 它会将给定的数据对象序列化后存储。
     *
     * @param operationType 操作类型
     * @param dataObject    待存储的数据对象
     */
    public SnackMQRaftLogEntry(OperationType operationType, Object dataObject) {
        this.operationType = operationType;
        this.data = serializer.serialize(dataObject);
    }

    /**
     * 从序列化数据中反序列化出指定类型的对象。
     *
     * @param clazz 指定的类类型
     * @param <T>   反序列化后的对象类型
     * @return 反序列化后的对象
     */
    public <T> T getData(Class<T> clazz) {
        return serializer.deserialize(this.data, clazz);
    }

    /**
     * 将当前日志条目对象序列化为字节数组。
     *
     * @return 序列化后的字节数组
     */
    public byte[] toBytes() {
        return serializer.serialize(this);
    }

    /**
     * 从字节数组中反序列化出 SnackMQRaftLogEntry 对象。
     *
     * @param bytes 字节数组
     * @return 反序列化后的 SnackMQRaftLogEntry 对象
     */
    public static SnackMQRaftLogEntry fromBytes(byte[] bytes) {
        return serializer.deserialize(bytes, SnackMQRaftLogEntry.class);
    }

    /**
     * 将自身打包成一个 sofa-jraft 可识别的 Task 对象。
     * 用于在 Raft 协议中执行操作。
     *
     * @param closure Raft 操作完成后的回调
     * @return 一个包含本日志条目数据的 Task
     */
    public Task toTask(Closure closure) {
        // 将自身序列化为字节数组，并放入 ByteBuffer
        final ByteBuffer dataBuffer = ByteBuffer.wrap(this.toBytes());
        // 创建 Task
        return new Task(dataBuffer, closure);
    }
}
